#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

# define SERV_PORT 9527

void sys_err(const char *str)
{
    perror(str);
    exit(1);
}

//运用select创建服务器
int main(int argc, char *argv[])
{
	int lfd, cfd;
	int i ,j, fret, nready;
	int maxfd = 0;
	char buf[4096], clit_ip[1024];
	
	struct sockaddr_in	serv_addr, clit_addr;
	socklen_t	clit_addr_len;

	bzero(&serv_addr, sizeof(serv_addr));//将地址空间清零，即初始化，也可以用memset函数
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(SERV_PORT);//本地字节序转网络字节序
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//任意可用ip地址

	lfd = socket(AF_INET,SOCK_STREAM,0);
	if(lfd == -1)
		sys_err("server socket create error");
	

	//设置端口复用
	int opt = 1;
	int ret = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR,&opt, sizeof(opt));
	if(ret == -1)
		sys_err("setsocketopt  error");
	

	ret = bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
	if(ret == -1)
		sys_err("server listen error");
	
	ret = listen(lfd, 128);
	if(ret == -1){
		sys_err("server listen error");
	}

	fd_set rset, allset;
	maxfd = lfd;

	FD_ZERO(&allset);
	FD_SET(lfd, &allset);

	while(1){
		rset = allset; //rset表示实际有那些请求，allset表示我想监听那些请求
		nready = select(maxfd+1, &rset, NULL, NULL, NULL);
		if (nready <0)
			sys_err("select error");
		
		if (FD_ISSET(lfd,&rset)){//上来先判断客户端是否向lfd发数据，有就调accept建立连接	

			clit_addr_len = sizeof(clit_addr);//传入只用求一次，放在循环里会反复求
			cfd = accept(lfd, (struct sockaddr *)&clit_addr, &clit_addr_len) ;
			if(cfd == -1)
				sys_err("server accept error");
			
			printf("client %d connected\n", cfd);
			printf("client ip:%s, port:%d\n", 
					inet_ntop(AF_INET, &clit_addr.sin_addr.s_addr, clit_ip, sizeof(clit_ip)),
					ntohs(clit_addr.sin_port));
			
			FD_SET(cfd,&allset); //将新创建的cfd加入select监听队列
			if (maxfd < cfd)
				maxfd = cfd;

			if (nready == 1)
			continue; //说明只有listen,后面对客户端读写操作不用执行
		}

		for (i = lfd+1; i <= maxfd; i++){ //lfd的处理完后对cfd的操作
			if (FD_ISSET(i, &rset)){  //检测那个cfd对客户端发送了数据
				
	    			fret = read(i,buf,sizeof(buf));
					if(fret == 0){
						printf("client %d closed\n", i);
						close(i);
						FD_CLR(i,&allset); //客户端已经断开连接，故从select中移除
					}

					write(STDOUT_FILENO, buf ,fret);

					for(j = 0; j<fret; j++){
						buf[j] = toupper(buf[j]);//对数据进行操作			
					}
					write(i,buf,fret);
			}
		}
	}
	close(lfd);
	return 0;
}

